#include <stdio.h>               
#include <stdlib.h>              
#include <string.h>              
#include <time.h>                
#include <ctype.h>               
#include <stdbool.h>             
#include <math.h>                
#include <sys/stat.h>            
#include <unistd.h>              
#include <sys/types.h>           
#include <dirent.h>              
#include <pwd.h>                 
#include <grp.h>                 
#include <fcntl.h>               
#include <sys/mman.h>            
#include <sys/select.h>          
#include <sys/time.h>            
#include <sys/wait.h>            
#include <sys/ipc.h>             
#include <sys/shm.h>             
#include <signal.h>//信号        
#include <pthread.h>//线程       
#include <arpa/inet.h>//网络     
#include <sys/socket.h>          
#include <netdb.h>               
#include <sys/epoll.h>           
#include <sys/sendfile.h>
#include <mysql/mysql.h>
#include <l8w8jwt/encode.h>
#include <l8w8jwt/decode.h>

//检查命令行参数数量是否符合预期
 #define ARGS_CHECK(argc, expected)\
    do{\
        if ((argc) != (expected)){\
            fprintf(stderr, "args num error!\n"); \
            exit(1); \
        }\
    }while(0)

                                               
// 检查返回值是否是错误标记,若是则打印msg和错误信
#define ERROR_CHECK(ret, error_flag, msg) \
    do { \
        if ((ret) == (error_flag)) { \
            perror(msg); \
            exit(1); \
        } \
    } while (0)



// 将消息写入日志文件的宏函数
#define LOG_WRITE(info_num, information)\
    info_write(info_num, information);





//自定网络传输协议
// 定义传输协议
typedef struct transport_protocol_s{
    int flag_stat; // 命令格式与内容是否正确的标记位
    int flag_work; // 执行命令的标记位
    int command_len; // 传输内容的长度
    char buf[60]; // 传输命令的内容   
    char curr_path[60]; //
    char user_id[20];//用户id
    MYSQL my_sql;//mysql 连接
    //
    int command;//
    char token[300];//
    
}transport_protocol_t;

//队列结构体以及基本操作
typedef struct QueueNode{               
    int data;                           
    transport_protocol_t msg;
    struct QueueNode*next;              
}QueueNode;                             
typedef struct QueueLink{               
    QueueNode *head;                    
    QueueNode*tail;                     
    int size;                           
                                        
}QueueLink;                             
void tail_insert(QueueLink*q,int data,transport_protocol_t msg); 
int enqueue(QueueLink*q,transport_protocol_t*msg);               
//---------------------------------------------

//线程共享数据                     
typedef struct SharedValue{        
    int num;                       
    pthread_t *plist;              
    QueueLink q;                   
    pthread_mutex_t lock_mutex;    
    pthread_cond_t cond;           
    int exit_flag;                 
}ShareValue;                       
//连接信息记录表
typedef struct connect_stat_s{
    int fd;
    time_t add_time;
}connect_stat_t;
//初始化公共资源以及线程池
int initpool(ShareValue*sv,int num);
//绑定端口ip，创建socket
int create_socket(int *socket_fd,const char*ip,const char*port);
//加入监听
int add_epoll(int epoll_fd,int add_fd);
//子进程入口函数
void *func(void*arg);
//子线程与客户交互
int dowork(int client_fd,transport_protocol_t msg);
//登录
int login(int fd,transport_protocol_t *msg);
//服务端 cd函数
//返回值：0:成功 -1:失败
//参数：net_fd文件标识符，filename:想去的文件夹名
int cd(int net_fd, transport_protocol_t* msg);

//获取文件hash值函数
//参数file_name为文件名
//传入传出参数hash_val，接收返回计算好的hash值
int getHash(const char* file_name,char *hash_val);

//传输文件
//第一个参数网络socket
//第二个参数文件路径
int sendFile(int socket_fd,transport_protocol_t*msg);

//服务端接收文件函数
//第一个参数网络socket
//第二个参数要接收的文件名
int recvFile(int socket_fd,transport_protocol_t*msg);

// 调用mkdir函数
// 参数1，文件描述符fd
// 参数2，协议结构体
int create_dir(int fd, transport_protocol_t*msg);
//服务端接收ls命令
//第一个参数网络socket
//第二个参数文件路径
void ls(int sockfd, transport_protocol_t*msg);

//服务端接收rm命令
//第一个参数网络scoket
//第二个参数文件名（文件路径）
void rm(int sockfd, transport_protocol_t *msg);

//创建一个数据库连接
//参数为MYSQL类型的变量
//ret，返回值0为成功，-1为失败。
int create_mysql(MYSQL *my_sql);

// 检测执行语句返回是否为空(ser_mkdir定义)
// 参数1：mysql 指针
// 返回0说明该目录存在，返回1说明查询语句返回结果为空
int sql_watch(MYSQL *mysqls);







// 获取当前时间的函数
// 参数1，需要放时间的字符串
int get_time(char *now_time);

// 将消息写进日志文件
// 参数1，信息等级，3为最严重，决定写入那个文件
// 参数2，写入什么信息
int info_write(int info_num, char *info);

// 获取配置文件的参数的函数
// 参数1，需要获取参数的名字
// 参数2，获取参数后放置的变量
int getparameter(char *key, char *value);


// 删除文件夹
// 参数1， 文件描述符fd
// 参数2，自定义协议指针
int ser_rmdir(int fd, transport_protocol_t *test);




//随机生成给定长度的字符串
//参数字符串长度
char *generate_random_string(int length);



//第四期新增函数
//解析token
//第一个参数token值
//第二个参数传入传出参数用户名
//第三个参数传入传出密码
int decrypt(char *str,char **username,char **password);


//制作token
//第一个参数用户名
//第二参数密码
//第三个参数制作好的token值
int ser_jwt(char *username,char *password,char **f_jwt);


//根据解析token函数得到的用户名密码取查表，并且获得user_id
//第一个参数，mysql连接
//第二个参数用户名
//第三个参数密码
//第四个参数user_id
int is_true(MYSQL *mysql,char*name,char*pass,char**user_id);

//直接调用这个函数可以解析和查表
//第一个参数mysql连接
//第二参数token
//第三个参数传入传出的user_id
int analysis_is(MYSQL *mysql,char*str,char **user_id);

//获取绝对路径（ls+地址用到）
char* getaa(transport_protocol_t *ps, char *ret);














// 删除文件夹
// 参数1， 文件描述符fd
// 参数2，自定义协议指针
int ser_rmdir(int fd, transport_protocol_t *test);















